
#include <BTcpSocket>
#include <WS2tcpip.h>
#include "member_BSocket.h"
#include "member_BTcpSocket.h"

using namespace BWE;

#define member					(*(member_BTcpSocket*)_ptr)
#define member_allocate()		_ptr = new member_BTcpSocket
#define member_release()		delete (member_BTcpSocket*)_ptr

#ifdef _LINUX_
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

BTcpSocket::BTcpSocket()
{
	member_allocate();
	socket_member(this)->create(AF_INET, SOCK_STREAM, 0);
}
BTcpSocket::~BTcpSocket()
{
	socket_member(this)->destroy();
	member_release();
}

BTcpSocket::Type BTcpSocket::type() const
{
	return Type_TcpSocket;
}

bool BTcpSocket::bind(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip.cstr());
	
	int ret = ::bind(socket_member(this)->socket, (sockaddr*)&addr, sizeof(addr));
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("bind");
		return false;
	}
	return true;
}

bool BTcpSocket::connect(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip.cstr());
	
	int ret = ::connect(socket_member(this)->socket, (sockaddr*)&addr, sizeof(addr));
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("connect");
		return false;
	}
	return true;
}

bool BTcpSocket::disconnect()
{
	int ret = ::shutdown(socket_member(this)->socket, SHUT_RDWR);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("shutdown");
		return false;
	}
	return true;
}
bool BTcpSocket::isConnected() const
{
	return state() == BSocket::State_Connected;
}

BSocket::Notifier BTcpSocket::select(double seconds) const
{
	fd_set rfds;
	fd_set wfds;
	fd_set efds;
	
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	
	FD_SET(socket_member(this)->socket, &rfds);
	//FD_SET(socket_member(this)->socket, &wfds);
	FD_SET(socket_member(this)->socket, &efds);

	timeval* ptv = 0;
	timeval tv;

	if(seconds >= 0)
	{
		tv.tv_sec = seconds;
		tv.tv_usec = (seconds - tv.tv_sec) * 1000000;
		ptv = &tv;
	}

	int ret = ::select(socket_member(this)->socket + 1, &rfds, &wfds, &efds, ptv);
	if(ret == -1)
	{
		perror("select");
		return BSocket::Notifier_Error;
	}
	if(FD_ISSET(socket_member(this)->socket, &rfds))
		return BSocket::Notifier_Read;
	if(FD_ISSET(socket_member(this)->socket, &wfds))
		return BSocket::Notifier_Write;
	if(FD_ISSET(socket_member(this)->socket, &efds))
		return BSocket::Notifier_Close;
	return BSocket::Notifier_None;
}

int BTcpSocket::size() const
{
	int ret = ::recv(socket_member(this)->socket, 0, 0, 0);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("recv for size");
	}
	return ret;
}
int BTcpSocket::recv(char* data, int length)
{
	int ret = ::recv(socket_member(this)->socket, data, length, 0);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("recv");
	}
	return ret;
}
int BTcpSocket::send(const char* data, int length)
{
	int ret = ::send(socket_member(this)->socket, data, length, 0);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("send");
	}
	return ret;
}

#endif

#ifdef _WINDOWS_SYSTEM_

BTcpSocket::BTcpSocket()
{
	member_allocate();
	socket_member(this)->create(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
BTcpSocket::~BTcpSocket()
{
	socket_member(this)->destroy();
	member_release();
}

BTcpSocket::Type BTcpSocket::type() const
{
	return TcpSocket;
}

bool BTcpSocket::bind(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	InetPton(AF_INET, ip.cstr(), &addr.sin_addr.s_addr);

	int ret = ::bind(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == SOCKET_ERROR)
	{
		int errorCode = WSAGetLastError();
		char* errStr = strerror(errno);
		perror("bind");
		return false;
	}
	return true;
}

bool BTcpSocket::connect(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	InetPton(AF_INET, ip.cstr(), &addr.sin_addr.s_addr);

	int ret = ::connect(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		return false;
	}
	socket_member(this)->state = State_Connecting;
	return true;
}

bool BTcpSocket::disconnect()
{
	int ret = ::closesocket(socket_member(this)->sock);
	if(ret == SOCKET_ERROR)
	{
		char* errStr = strerror(errno);
		perror("close socket");
		return false;
	}
	return true;
}

int BTcpSocket::recv(char* data, int length)
{
	int ret = ::recv(socket_member(this)->sock, data, length, 0);
	if(ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		socket_member(this)->state = BSocket::State_Closed;
		emit(Signal_Disconnected, this);
		return 0;
	}
	if (ret == 0)
	{
		socket_member(this)->state = BSocket::State_Closed;
		emit(Signal_Disconnected, this);
	}
	return ret;
}
int BTcpSocket::send(const char* data, int length)
{
	int ret = ::send(socket_member(this)->sock, data, length, 0);
	if(ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		socket_member(this)->state = BSocket::State_Closed;
		emit(Signal_Disconnected, this);
	}
	return ret;
}

int BTcpSocket::recv(BByteArray& bytes)
{
	while (1)
	{
		BByte buffer[1024];
		int size = ::recv(socket_member(this)->sock, (char*)buffer, 1024, 0);
		if (size < 0)
		{
			int errorCode = WSAGetLastError();
			socket_member(this)->state = State_Closed;
			break;
		}
		if (size == 0)
		{
			int errorCode = WSAGetLastError();
			socket_member(this)->state = State_Closed;
			break;
		}
		if (size > 0)
		{
			bytes.append(buffer, size);
		}
		if (size < 1024)
		{
			break;
		}
	}
	return bytes.size();
}
int BTcpSocket::send(const BByteArray& bytes)
{
	return send((char*)bytes.data(), bytes.size());
}

#endif
